package net.webparation.portlet;

import java.io.IOException;
import java.util.HashMap;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

/**
 * Abstract class for implementing a contorller portlet.
 *
 * @author <a href="mailto:allan.lykke.christensen@gmail.com">Allan Lykke Christensen</a>
 */
public abstract class ControllerPortlet extends GenericPortlet implements SimplePortlet {
    
    /** Registry of views. */
    protected HashMap<String, ViewCommand> views = new HashMap<String, ViewCommand>();
    
    /** Registry of actions. */
    protected HashMap<String, ActionCommand> actions = new HashMap<String, ActionCommand>();
    
    /** Name of the default action or view. */
    public final static String DEFAULT_COMMAND = "DEFAULT";
    
    /**
     * Sets up the actions and views upon initialisation.
     *
     * @see javax.portlet.GenericPortlet#init(javax.portlet.PortletConfig)
     */
    public void init (PortletConfig config) throws PortletException {
        super.init (config);
        setupActions ();
        setupViews ();
    }
    
    /**
     * Rendered when the portlet mode is PortletMode.VIEW
     *
     * @see GenericPortlet#doView(RenderRequest, RenderResponse)
     */
    protected void doView (RenderRequest request, RenderResponse response) throws PortletException, IOException {
        // Determine view to execute
        String viewName = request.getParameter ("action");
        viewName = viewName == null ? DEFAULT_COMMAND : viewName;
        
        getPortletContext ().log ("View to execute: " + viewName);
        
        // Look-up and execute view
        ViewCommand vc = lookupView (viewName);
        String dispatchFile = vc.execute (request, response);
        
        // Despatch the view
        dispatchView (request, response, dispatchFile);
    }
    
    /**
     * Processes the {@link ActionCommand} matching the <code>action</code>
     * parameter.
     *
     * @see GenericPortlet#processAction(ActionRequest, ActionResponse)
     */
    public void processAction (ActionRequest request, ActionResponse response)  throws PortletException, IOException {
        PortletMode mode = request.getPortletMode ();
        response.setPortletMode (mode);
        
        if (mode.equals (PortletMode.VIEW)) {
            String actionName = request.getParameter ("action") == null ? DEFAULT_COMMAND : request.getParameter ("action");
            getPortletContext ().log ("Action to execute: " + actionName);
            
            // Lookup and execute actions
            ActionCommand ac = lookupAction (actionName);
            ac.execute (request, response);
        }
    }
    
    /**
     * Dispatches a view (JSP).
     *
     * @param request {@link RenderRequest} to include in dispatch
     * @param response (@link RenderResponse} to include in dispatch
     * @param dispatchFile Name of the JSP file to dispatch.
     */
    private void dispatchView (RenderRequest request, RenderResponse response, String dispatchFile) throws PortletException, IOException {
        PortletRequestDispatcher dispatch = getPortletContext ().getRequestDispatcher (dispatchFile);
        dispatch.include (request, response);
    }
    
    /**
     * Lookup the {@link ViewCommand} matching the <code>viewName</code> in the
     * view registry. If a {@link ViewCommand} matching the
     * <code>viewName</code> could not be found, the <code>DEFAULT</code>
     * {@link ViewCommand} is returned.
     *
     * @param viewName Name of the view to lookup
     * @return {@link ViewCommand} matching the <code>viewName</code>, or the
     *  <code>DEFAULT</code> {@link ViewCommand} if there were no match.
     */
    private ViewCommand lookupView (String viewName) {
        
        ViewCommand vc = null;
        
        if (this.views.containsKey (viewName)) {
            vc = (ViewCommand) this.views.get (viewName);
        } else {
            getPortletContext ().log ("View '" + viewName + "' does not exist in registry");
            getPortletContext ().log ("Executing " + DEFAULT_COMMAND);
            
            vc = (ViewCommand) this.views.get (DEFAULT_COMMAND);
        }
        
        return vc;
    }
    
    /**
     * Lookup the {@link ActionCommand} matching the <code>actionName</code> in
     * the action registry. If an {@link ActionCommand} matching the
     * <code>actionName</code> could not be found, the <code>DEFAULT</code>
     * {@link ActionCommand} is returned.
     *
     * @param actionName Name of the action to lookup
     * @return {@link ActionCommand} matching the <code>actionName</code>, or
     *  the <code>DEFAULT</code> {@link ActionCommand} if there were no match.
     */
    private ActionCommand lookupAction (String actionName) {
        
        ActionCommand ac = null;
        
        if (this.actions.containsKey (actionName)) {
            ac = (ActionCommand) this.actions.get (actionName);
        } else {
            getPortletContext ().log ("Action '" + actionName + "' does not exist in registry");
            getPortletContext ().log ("Executing " + DEFAULT_COMMAND);
            
            ac = (ActionCommand) this.actions.get (DEFAULT_COMMAND);
        }
        
        return ac;
    }
    
    
    
}
